package com.zzz.springsecuritystudy.util;

import com.zzz.springsecuritystudy.constant.JwtConstant;
import com.zzz.springsecuritystudy.constant.SecurityConstants;
import com.zzz.springsecuritystudy.entity.CheckResult;
import com.zzz.springsecuritystudy.entity.Role;
import com.zzz.springsecuritystudy.entity.User;
import io.jsonwebtoken.*;
import lombok.Getter;
import lombok.Setter;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zhuzhizun
 * @date 2021/8/4
 */
@Component
@Getter
@Setter
public class JwtUtils {


    private static String secret  = "OTk3OTEwenp6"; // 密钥


    private static String subject = "authentication";  // 主题


    private static String issuer = "zzz"; // 签发者

    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.decode(secret);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }


    /**
     * 解析jwt
     * @param jwt token
     * @return Claims
     */
    public static Claims parseJWT(String jwt) {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

    public static String createToken(Authentication authentication,long ttlMillis){
        User user = (User)authentication.getPrincipal();
        List<String> roleList = user.getRole().stream().map(Role::getRoleName).collect(Collectors.toList());
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        HashMap<String, Object> claim = new HashMap<>();
        claim.put(SecurityConstants.ROLE_CLAIMS,String.join(",",roleList));
        JwtBuilder builder = Jwts.builder()
                .setClaims(claim)
                .setId(user.getId().toString())
                // 主题
                .setSubject(user.getUsername())
                // 签发者
                .setIssuer(issuer)
                // 签发时间
                .setIssuedAt(now)
                // 签名算法以及密匙
                .signWith(signatureAlgorithm, secretKey);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            // 过期时间
            builder.setExpiration(expDate);
        }
        return SecurityConstants.TOKEN_SPLIT+builder.compact();


    }

    /**
     * 创建 通用 createJWT
     * @param info  放入claim的信息
     * @param ttlMillis 过期时间
     * @return token令牌
     */
    public static String createJWT(String info, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        HashMap<String, Object> claim = new HashMap<>();
        claim.put("info",info);
        JwtBuilder builder = Jwts.builder()
                .setClaims(claim)
                // 主题
                .setSubject(subject)
                // 签发者
                .setIssuer(issuer)
                // 签发时间
                .setIssuedAt(now)
                // 签名算法以及密匙
                .signWith(signatureAlgorithm, secretKey);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            // 过期时间
            builder.setExpiration(expDate);
        }
        return builder.compact();
    }

    /**
     * 验证JWT
     * @param jwtStr
     * @return
     */
    public static CheckResult validateJWT(String jwtStr) {
        CheckResult checkResult = new CheckResult();
        Claims claims = null;
        try {
            claims = parseJWT(jwtStr);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setErrCode(JwtConstant.TOKEN_ERR_CODE_EXPIRE);
            checkResult.setSuccess(false);
        } catch (SignatureException e) {
            checkResult.setErrCode(JwtConstant.TOKEN_ERR_CODE_NULL);
            checkResult.setSuccess(false);
        } catch (Exception e) {
            checkResult.setErrCode(JwtConstant.TOKEN_ERR_CODE_FAIL);
            checkResult.setSuccess(false);
        }
        return checkResult;
    }


}
